#include "Page.h"
#include "Triangle.h"
#include "LCC.h"
#include "OgreManualObject.h"
#include "OgreMesh.h"
#include "OgreEntity.h"
#include "OgreSceneManager.h"
#include "OgreProgressiveMesh.h"
#include "OgreDistanceLodStrategy.h"
#include "OgreMeshSerializer.h"
#include "OgreMeshManager.h"
#include "OgreInitializeSettings.h"
#include "FileOps.h"
#include <iostream>

Page::Page(double minX, double minZ, double maxX, double maxZ, Ogre::SceneManager* manager, int lods, OgreInitializeSettings* init, int id, Ogre::ResourceGroupManager* rmanager, const char* tername){
	this->minX = minX;
	this->minZ = minZ;
	this->maxX = maxX;
	this->maxZ = maxZ;
	this->manager = manager;
	this->init = init;
	this->rmanager = rmanager;
	this->lods = lods;
	this->mm = new Ogre::MeshManager();
	ms = new Ogre::MeshSerializer();

	char* a = FileOps::integerToString(id, 10);
	name = std::string(tername)+"Page"+std::string(a);
	delete[] a;
	path = init->relative_path + "\\Terrain\\"+tername+"\\";
	path += name;
	path += "\\";
	FileOps::createDirectory(path.c_str());

	for (int i = 0; i < lods; i++){
		std::multimap<LCC, Triangle*>* lod = new std::multimap<LCC, Triangle*>();
		maps.push_back(lod);
		char* ca = FileOps::integerToString(i, 10);
		this->rmanager->createResourceGroup(name+"tile"+ca);
		delete[] ca;
	}
}

Page::~Page(){
	//cleanup();
	removeAllLods();
	deleteAllLods();
	for (int i = 0; i < lods; i++){
		char* ca = FileOps::integerToString(i, 10);
		rmanager->destroyResourceGroup(name+"tile"+ca);
		delete[] ca;
	}

	delete ms;
	delete mm;
}

double* Page::getCoords(){
	double* dim = new double[4];
	dim[0] = minX;
	dim[1] = minZ;
	dim[2] = maxX;
	dim[3] = maxZ;
	return dim;
}

void Page::fillMap(LCC type, Triangle *tri, unsigned char lod){
	maps[lod-1]->insert(std::pair<LCC, Triangle*>(type, tri));
	//std::cout << (int) lod << " is the current lod!111\n";
};

void Page::compile(){
	int size = maps.size();
	Ogre::AxisAlignedBox aab (Ogre::Vector3(minX, minY, minZ), Ogre::Vector3(maxX, maxY, maxZ));
	for (int curlod = 0; curlod < size; curlod++){
		Ogre::ManualObject* ter = manager->createManualObject();
		std::cout << maps[curlod]->size();
		const char* matname = "BaseWhite";	
		std::multimap<LCC, Triangle*>::iterator it = maps[curlod]->lower_bound(pasture);
		std::multimap<LCC, Triangle*>::iterator end = maps[curlod]->upper_bound(pasture);
		unsigned int i = 0;
		ter->begin(matname, Ogre::RenderOperation::OT_TRIANGLE_LIST);
		while (it != end){
			Triangle* tri = it->second;
			ter->position(tri->a);
			ter->position(tri->b);
			ter->position(tri->c);
			ter->triangle(i, i+1, i+2);
			i += 3;
			it++;
		}
		ter->end();

		std::string s = name+"tile";
		const char* text = FileOps::integerToString(curlod, 10);
		s += text;
		delete[] text;
		std::cout << s <<"\n";
		char* ca = FileOps::integerToString(curlod, 10);
		std::string rs = name+"tile"+ca;
		Ogre::Mesh* curmesh = ter->convertToMesh(s.c_str(), rs.c_str()).getPointer();
		delete[] ca;
		//curmesh->_setBounds(aab);
		//curmesh->_setBoundingSphereRadius((maxX - minX) / 2);

		char* lodstring = FileOps::integerToString(curlod, 10);
		std::string lodstd (lodstring);
		delete[] lodstring;
		std::string exp = path+name+"lod"+lodstd;
		exp += ".mesh";
		ms->exportMesh(curmesh, exp);
		curmesh->unload();
		delete ter;
	}
	//Ogre::DistanceLodStrategy* dist = new Ogre::DistanceLodStrategy();
	//mesh->setLodStrategy(dist);
	double radius = (maxX - minX) / 2;
	cleanup();
}

void Page::setElevationExtrema(double min, double max){
	this->minY = min;
	this->maxY = max;
	double averageY = (min + max) / 2;
	Ogre::Vector3 a (minX, averageY, minZ);
	Ogre::Vector3 c (maxX, averageY, maxZ);
	middle = a.midPoint(c);
}

void Page::cleanup(){
	for(std::vector<std::multimap<LCC, Triangle*>*>::iterator it = maps.begin(); it != maps.end(); it++){
		std::multimap<LCC, Triangle*>* curmap = *it;
		for (std::multimap<LCC, Triangle*>::iterator jt = curmap->begin(); jt != curmap->end(); jt++){
			delete jt->second;
		}
		delete curmap;
	}
}

void Page::printCoords(){
	std::cout << minX << " is minX\n";
	std::cout << minY << " is minY\n";
	std::cout << minZ << " is minZ\n";

	std::cout << maxX << " is maxX\n";
	std::cout << maxY << " is maxY\n";
	std::cout << maxZ << " is maxZ\n";
}

void Page::removeLod(int lod){
	Ogre::Entity* ent = entities.at(lod);
	ent->detachFromParent();
	delete ent;
	entities.erase(entities.begin()+lod);
	char* ca = FileOps::integerToString(lod, 10);
	std::string rname = name + "tile" + ca;
	delete[] ca;
	rmanager->unloadResourceGroup(rname, false);
}

void Page::removeAllLods(){
	while(!entities.empty()){
		removeLod(0);
	}
}

void Page::deleteLod(int lod){
	char* ca = FileOps::integerToString(lod, 10);
	std::string rp = path+name+"lod"+std::string(ca)+".mesh";
	delete[] ca;
	FileOps::deleteFile(rp.c_str());
}

void Page::deleteAllLods(){
	for (int i = 0; i < lods; i++){
		deleteLod(i);
	}
}

void Page::loadLod(int lod){
	std::string s = name+"tile";
	const char* text = FileOps::integerToString(lod, 10);
	s += text;
	std::string file = path+"tile"+text;
	std::string group = name+"tile"+text;
	delete[] text;
	mm->load(file, group);
}

void Page::loadAllLods(){
	loadLodFrom(0);
}

void Page::loadLodFrom(int lod){
	for (int i = lod; i < lods; i++){
		loadLod(i);
	}
}